#include "sync.h"

void * SyncMaintenanceLoop(void *sync) {
  ((Sync *)sync)->MaintenanceLoop();
  CHECK(false);
  return NULL;
}

void Sync::StartWork(){
  pthread_mutex_lock(&mutex_);
  while (needs_maintenance_) {
    pthread_cond_wait(&work_cond_, &mutex_);
  }
  num_workers_running_++;
  // cout << "Starting work - #workers = " << num_workers_running_ << endl;
  pthread_mutex_unlock(&mutex_);
}
void Sync::StopWork(){
  pthread_mutex_lock(&mutex_);
  num_workers_running_--;
  // cout << "Stopping work - #workers = " << num_workers_running_ << endl;
  if (num_workers_running_ == 0) pthread_cond_signal(&maintenance_cond_);
  pthread_mutex_unlock(&mutex_);
}
void Sync::CheckStop(){
  if (needs_maintenance_) throw SyncException();
}
void Sync::StopAll(){
  needs_maintenance_ = true;
  pthread_mutex_lock(&mutex_);
  pthread_cond_signal(&maintenance_cond_);
  pthread_mutex_unlock(&mutex_);
}

void Sync::StartMaintenance(){
  // cout << "Start Maintenance" << endl;
  pthread_mutex_lock(&mutex_);
  while ((num_workers_running_ > 0) || (!needs_maintenance_)) {
    pthread_cond_wait(&maintenance_cond_, &mutex_);
  }
  pthread_mutex_unlock(&mutex_);
}
void Sync::EndMaintenance(){
  cout << "End Maintenance" << endl;
  pthread_mutex_lock(&mutex_);
  needs_maintenance_ = false;
  pthread_cond_broadcast(&work_cond_);
  pthread_mutex_unlock(&mutex_);
}

// testing sync

Sync * test_sync;
uint64 val = 0;
uint64 next_thread_id = 0;

void * TestWorker(void * junk) {
  uint64 thread_id = __sync_fetch_and_add(&next_thread_id, 1);
  while (1) {
    try {
      Sync::Worker w(test_sync);
      while(1) {
	if (val == 1000) test_sync->StopAll();
	w.CheckStop();
	uint64 i = __sync_fetch_and_add(&val, 1);
	COUT << "Thread " << thread_id << " val=" << i << "\n";
      }
    } catch (SyncException) {
      COUT << "Thread "  << thread_id << " caught exception \n";
    }
  }
  return NULL;
}

void * TestMaintenance(void *junk) {
  COUT << "Resetting val to 0\n";
  val = 0;
  return NULL;
}

void TestSyncMain() {
  test_sync = new Sync();
   test_sync->AddMaintenanceRoutine(&TestMaintenance);
   for (uint64 i=0; i<10; i++) {
     pthread_t *thread = new pthread_t;
     pthread_create(thread, NULL, &TestWorker, NULL);
   }
 }

int test_sync_main() {
  TestSyncMain();
  sleep(1000000000);
  return 0;
}

/*
vector<uint64> A(1000);
RWLock RW;

void * TestRWLockThread(void *junk) {
  RWLock::Reader reader(&RW);
  while(1) {
    if (rand() % 10 == 0) {
      // writer
      RWLock::WriteLocker l(&reader);
      for (uint64 i=0; i<A.size(); i++) {
	A[i]++;
      }
      COUT << "Wrote array " << A[0] << "\n";
    } else {
      // reader
      RWLock::ReadLocker l(&reader);
      for (uint64 i=0; i<A.size(); i++) CHECK(A[i] == A[0]);	      
      COUT << "Read array " << A[0] << "\n";
    }
  }
  return NULL;
}


int test_rw_lock_main() {
  for (uint64 i=0; i<10; i++) {
    pthread_t *thread = new pthread_t;
    pthread_create(thread, NULL, &TestRWLockThread, NULL);
  }
  sleep(1000000);
  return 0;
}


int speed_test_main() { 
  RWLock::Reader reader(&RW);
  for (uint64 i=0; i<200000000; i++) {
    RWLock::ReadLocker l(&reader);    
  }
  return 0;
}

*/

/*int main() {
  return speed_test_main();  
  }*/
